package org.arch_learn.netty_rpc.provider2.handler;

import com.alibaba.fastjson.JSON;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.arch_learn.netty_rpc.netty_rpc_api.common.RpcRequest;
import org.arch_learn.netty_rpc.netty_rpc_api.common.RpcResponse;
import org.arch_learn.netty_rpc.provider2.anno.RpcService;
import org.springframework.beans.BeansException;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 1.将标有@RpcService的Bean缓存
 * 2.接收客户端的请求
 * 3.根据传过来的beanName从缓存中查找对应的bean
 * 4.解析请求中的方法名称，参数类型，参数值
 * 5.通过反射调用bean的方法
 * 6.给客户端进行响应
 */
@Component
@ChannelHandler.Sharable
public class NettyRpcServerHandler extends SimpleChannelInboundHandler<String> implements ApplicationContextAware {

    private static final Map RPC_SERVICE_INSTANCE_MAP = new ConcurrentHashMap();

    /**
     * 1.将标有@RpcService注解的bean缓存
     *
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Object> rpcServiceMap = applicationContext.getBeansWithAnnotation(RpcService.class);
        if (rpcServiceMap != null && rpcServiceMap.size() > 0) {
            rpcServiceMap.entrySet().forEach(entry -> {
                Object rpcServiceObj = entry.getValue();
                if (rpcServiceObj.getClass().getInterfaces().length == 0) {
                    throw new RuntimeException("rpc服务类必须实现接口！");
                }
                String name = rpcServiceObj.getClass().getInterfaces()[0].getName();
                //默认取第一个接口作为缓存bean的名称
                RPC_SERVICE_INSTANCE_MAP.put(name, rpcServiceObj);
            });
        }
    }

    /**
     * 处理通道读取就绪事件
     *
     * @param channelHandlerContext
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
        //2.接收客户端请求，将传入字符串转换为RpcRequest对象

        RpcRequest rpcRequest = JSON.parseObject(msg, RpcRequest.class);
        RpcResponse rpcResponse = new RpcResponse();
        rpcResponse.setResponseId(rpcRequest.getRequestId());
        try {
            Object result = handle(rpcRequest);
            rpcResponse.setResult(result);
        } catch (Exception e) {
            e.printStackTrace();
            rpcResponse.setError(e.getMessage());
        }
        //6.给客户端进行响应
        //这里添加"\n"作为消息体的结束符，这样客户端的解码器在读到结束符之后，才会调用channelRead0()方法接收消息。
        channelHandlerContext.writeAndFlush(JSON.toJSON(rpcResponse) + "\n");
    }

    /**
     * 获取业务处理
     */
    private Object handle(RpcRequest rpcRequest) throws InvocationTargetException {
        //3.根据传过来的beanName从缓存中查找对应的bean
        Object rpcServiceBean = RPC_SERVICE_INSTANCE_MAP.get(rpcRequest.getClassName());
        if (null == rpcServiceBean) {
            throw new RuntimeException("根据beanName：" + rpcRequest.getClassName() + "找不到rpc服务！");
        }

        //4.解析请求中的方法名称，参数类型，参数值
        Class<?> rpcServiceBeanClass = rpcServiceBean.getClass();
        String methodName = rpcRequest.getMethodName();
        Object[] parameters = rpcRequest.getParameters();
        Class<?>[] parameterTypes = rpcRequest.getParameterTypes();

        //5.通过反射调用bean的方法--CGlib反射调用
        FastClass fastClass = FastClass.create(rpcServiceBeanClass);
        FastMethod method = fastClass.getMethod(methodName, parameterTypes);
        Object result = method.invoke(rpcServiceBean, parameters);
        return result;
    }

}
